﻿using System;

namespace SoftwareConsulting.BI11.NumericalMethods.Statistics
{    
    /// <summary>
    /// Генератор случайных чисел,  а также случайных чисел, распределенных по Гауссу
    /// </summary> 
    public class RandomGaussianGenerator
    {
        private bool _haveNextNextGaussian;
        private double _nextNextGaussian;
        private Random _random;

        /// <summary>
        /// Конструктор класса
        /// </summary>
        public RandomGaussianGenerator()
        {
            _random = new Random();
            _haveNextNextGaussian = false;
        }

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="seed"></param>
        public RandomGaussianGenerator(int seed)
        {
            SetSeed(seed);
        }

        /// <summary>
        /// Устанавливает новое значение уникальности для генератора
        /// </summary>
        public void SetSeed(int newSeed)
        {
            _random = new Random(newSeed);
            _haveNextNextGaussian = false;            
        }

        /// <summary>
        /// Возвращает следующее случайное вещественное число
        /// </summary>
        /// <returns></returns>
        public double NextDouble()
        {
            return _random.NextDouble();
        }

        /// <summary>
        /// Возвращает следующее случайное вещественное число, распределенное по Гауссу
        /// </summary>
        /// <returns></returns>
        public double NextGaussian()
        {
            lock (_random)
            {
                if (_haveNextNextGaussian)
                {
                    _haveNextNextGaussian = false;
                    return _nextNextGaussian;
                }                
                double v1, v2, s;
                do
                {
                    v1 = 2 * _random.NextDouble() - 1;//в промежутке [-1;1]
                    v2 = 2 * _random.NextDouble() - 1;//в промежутке [-1;1]
                    s = v1 * v1 + v2 * v2;
                } 
                while (s >= 1 || s == 0);
                double multiplier = Math.Sqrt(-2 * Math.Log(s) / s);
                _nextNextGaussian = v2 * multiplier;
                _haveNextNextGaussian = true;
                return v1 * multiplier; 
            }
        }
    }
}
